/*
 * # 应用所有存储过程:
 *  Log - 日志
 * 
 * # 描述:
 *  向开发者提供记录数据操作日志消息的服务，同时向系统管理人员提供查看和管理已经记录的数据操作日志消息的功能。
 *  数据操作的日志消息将记录在数据库表中，如果开发者需要调试、跟踪或异常错误等日志消息，应该使用日志文件。
 * 
 * Copyright (c), 上海沃恩信息科技有限公司.
 * All rights reserved.
 * Licensed under BSD (https://www.pittypat.work/bsd.html).
 */
 
use "pittypat_app";
go

/*
 * # 存储过程 - Sql Server:
 *   Create - 写入数据操作记录
 * 
 * # 描述:
 *   创建一条新的数据操作日志记录。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_log"."p_op_create"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_log"."p_op_create" as begin set nocount on; end');
go

alter procedure "waln_log"."p_op_create"
	@userId uniqueidentifier, -- 用户 Id 
	@opType tinyint, -- 操作类型代码 
	@tableNames varchar(200), -- 数据库表名称 
	@procName varchar(128), -- 存储过程名称 
	@success bit, -- 是否成功 
	@summary nvarchar(200), -- 操作摘要 
	@body nvarchar(max), -- 消息正文 
	@id bigint out, -- 消息 Id 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	-- 默认指示操作失败
	set @_success = 0;
	set @_message = N'';

	-- 初始化 out 参数 @id (消息 Id)
	set @id = null;
	
    -- 验证参数 $opType (操作类型代码：1-查询、2-添加、4-更新、8-删除、16-其他)，可以是多个值的组合
	if @opType is null
	begin
		set @opType = 16;
	end

	-- 验证参数 @success (是否成功)
	if @success is null
	begin
		set @success = 1;
	end

	-- 验证参数 @summary (操作摘要)
	if @summary is null or @summary = ''
	begin
		set @summary = case when @body is null then null else left(@body, 200) end;
	end

	insert into "waln_log"."opmessages"
	(
		"UserId",
		"OpType",
		"TableNames",
		"ProcName",
		"Success",
		"Summary",
		"Body",
		"OpTime"
	)
	values
	(
		@userId,
		@opType,
		@tableNames,
		@procName,
		@success,
		@summary,
		@body,
		sysdatetime()
	);

	-- 返回 Create.id
	set @id = scope_identity();

	-- 指示操作成功
	set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   Delete - 删除数据操作日志消息
 * 
 * # 描述:
 *   删除一条数据操作日志消息，仅仅在当前会话范围内使用。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_log"."p_op_delete"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_log"."p_op_delete" as begin set nocount on; end');
go

alter procedure "waln_log"."p_op_delete"
	@userId uniqueidentifier, -- 用户 Id 
	@id bigint, -- 消息 Id 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	-- 默认指示操作失败
	set @_success = 0;
	set @_message = N'';

	-- 验证参数 @id (消息 Id)
	if @id is null
	begin
		set @_message = N'没有提供 消息 Id。';
		return;
	end

	if @userId is null
    begin
		delete from 
			"waln_log"."opmessages" 
		where 
			"Id" = @id and
            "UserId" is null; 
    end
    else
    begin
		delete from 
			"waln_log"."opmessages" 
		where 
			"Id" = @id and
            "UserId" = @userId; 
    end

	-- 指示操作成功
	set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   List - 数据操作日志列表
 * 
 * # 描述:
 *   查询和显示数据操作日志列表。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_log"."p_mop_list"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_log"."p_mop_list" as begin set nocount on; end');
go

alter procedure "waln_log"."p_mop_list"
	@pageIndex bigint, -- 页码 
	@pageSize bigint, -- 页面尺寸 
	@totalRows bigint out, -- 总行数 
	@orderName nvarchar(30), -- 排序名称 
	@orderDirection nvarchar(4), -- 排序方向 
	@administratorId uniqueidentifier, -- 管理员用户 Id 
	@userName nvarchar(50), -- 用户名 
	@opType tinyint, -- 操作类型代码 
	@timeFrom datetime2, -- 起始操作时间 
	@timeTo datetime2, -- 截至时间 
	@tableNames varchar(200), -- 数据库表名称 
	@procName varchar(128), -- 存储过程名称 
	@success bit, -- 是否成功 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	-- 默认指示操作失败
	set @_success = 0;

	-- 初始化 out 参数 @totalRows (总行数)
	set @totalRows = 0;
	
	-- 验证参数 @administratorId (管理员用户 Id)
	if @administratorId is null
	begin
		set @_message = N'没有提供有效的 管理员用户 Id。';
		return;
	end

	-- 验证参数 @pageIndex (页码)
	if @pageIndex is null or @pageIndex < 1
	begin
		-- 默认分页查询的页码，从 1 开始
		set @pageIndex = 1;
	end

	-- 验证参数 @pageSize (页面尺寸)
	if @pageSize is null or @pageSize < 1
	begin
		-- 默认分页查询的页面尺寸
		set @pageSize = 20;
	end

	-- 验证参数 @orderName (排序名称)
	if @orderName is null or @orderName = '' or @orderName not in 
	(
		-- 确保排序名称是以下名称之一，防止 SQL 注入式攻击
		'OpType', 
		'OpTime', 
		'TableNames', 
		'ProcName', 
		'Success'
	)
	begin
		-- 默认排序列
		set @orderName = 'OpTime';
	end

	-- 验证参数 @orderDirection (排序方向)
	if @orderDirection is null or @orderDirection not in ('asc', 'desc')
	begin
		-- 默认排序方向
		set @orderDirection = 'desc';
	end

	if @timeTo is not null 
	begin
		set @timeTo = dateadd("DAY", 1, @timeTo);
	end

	-- 返回 List.messages
	-- 为 like 运算转义字符串参数 @userName
	set @userName = "waln_log"."like_escape"(@userName);
	-- 为 like 运算转义字符串参数 @tableNames
	set @tableNames = "waln_log"."like_escape"(@tableNames);
	-- 为 like 运算转义字符串参数 @procName
	set @procName = "waln_log"."like_escape"(@procName);

	-- 参数 @totalRows (总行数)
	select 
		@totalRows = count_big(*) 
	from 
		"waln_log"."v_mop_opmessage" as v 
	where 
		((@userName is null) or (v."UserName" like @userName)) and 
		((@opType is null) or ((v."OpType" & @opType) <> 0)) and 
		((@timeFrom is null) or (v."OpTime" >= @timeFrom)) and 
		((@timeTo is null) or (v."OpTime" < @timeTo)) and 
		((@tableNames is null) or (v."TableNames" like @tableNames)) and 
		((@procName is null) or (v."ProcName" like @procName)) and 
		((@success is null) or (v."Success" = @success)); 

	-- 没有满足条件的记录行
	if @totalRows is null or @totalRows < 1
	begin
		set @totalRows = 0;
		-- 指示操作成功
		set @_success = 1;
		return;
	end

	-- 动态查询命令文本
	declare @_page_sql nvarchar(4000);
	-- 动态查询命令参数定义文本
	declare @_params_def nvarchar(4000);
	-- 分页用的起始行号，从 1 开始
	declare @_start_row bigint;
	-- 分页用的截至行号，从 1 开始
	declare @_end_row bigint;

	-- 根据页码和页面尺寸计算起始行号
	set @_start_row = 1;
	if @pageIndex > 1
	begin
		set @_start_row = (@pageIndex - 1) * @pageSize + 1;
	end

	-- 根据页面尺寸和起始行号计算截至行号
	set @_end_row = @_start_row + @pageSize - 1

	-- 动态创建查询命令文本
	set @_page_sql = 
		'select ' + 
			'__pv.* ' + 
		'from ' + 
		'(' + 
			'select ' + 
				'v.*, ' + 
				'(row_number() over(order by ' + @orderName + ' ' + @orderDirection + ')) as __row_no ' + 
			'from ' + 
			'"waln_log"."v_mop_opmessage" as v '+ 
			'where ' + 
				'((@userName is null) or (v."userName" like @userName)) and ' + 
				'((@opType is null) or ((v."OpType" & @opType) <> 0)) and ' + 
				'((@timeFrom is null) or (v."OpTime" >= @timeFrom)) and ' + 
				'((@timeTo is null) or (v."OpTime" < @timeTo)) and ' + 
				'((@tableNames is null) or (v."TableNames" like @tableNames)) and ' + 
				'((@procName is null) or (v."ProcName" like @procName)) and ' + 
				'((@success is null) or (v."Success" = @success)) ' + 
		') as __pv ' + 
		'where ' + 
			'__row_no >= ' + cast(@_start_row as varchar(20)) + ' and __row_no <= ' + cast(@_end_row as varchar(20));

	set @_params_def = 
		'@userName nvarchar(50),' + 
		'@opType tinyint,' + 
		'@timeFrom datetime2,' + 
		'@timeTo datetime2,' + 
		'@tableNames varchar(200),' + 
		'@procName varchar(128),' + 
		'@success bit'; 

	exec "dbo"."sp_executesql" @_page_sql, @_params_def, 
		@userName, 
		@opType, 
		@timeFrom, 
		@timeTo, 
		@tableNames, 
		@procName, 
		@success;

	-- 指示操作成功
	set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   Details - 详细信息
 * 
 * # 描述:
 *   查询和显示某一条日志消息的详细说明信息。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_log"."p_mop_details"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_log"."p_mop_details" as begin set nocount on; end');
go

alter procedure "waln_log"."p_mop_details"
	@administratorId uniqueidentifier, -- 管理员用户 Id 
	@id bigint, -- 消息 Id 
	@body nvarchar(max) out, -- 消息正文 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	-- 默认指示操作失败
	set @_success = 0;
	set @_message = N'';

	-- 初始化 out 参数 @body (消息正文)
	set @body = null;

	-- 验证参数 @administratorId (管理员用户 Id)
	if @administratorId is null
	begin
		set @_message = N'没有提供有效的 管理员用户 Id。';
		return;
	end

	-- 验证参数 @id (消息 Id)
	if @id is null
	begin
		set @_message = N'没有提供 消息 Id。';
		return;
	end

	-- 返回 Details.body
	select @body = "Body" from "waln_log"."opmessages" where "Id" = @id;

	if @body is null or @body = ''
	begin
		set @body = N'[没有提供更详细的操作说明]';
	end
	
	-- 指示操作成功
	set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   Delete - 删除操作消息
 * 
 * # 描述:
 *   删除一条或多条数据操作日志消息。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_log"."p_mop_delete"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_log"."p_mop_delete" as begin set nocount on; end');
go

alter procedure "waln_log"."p_mop_delete"
	@administratorId uniqueidentifier, -- 管理员用户 Id 
	@ids nvarchar(max), -- 消息 Id 列表 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint;
	declare @_log_body nvarchar(4000) = null;
	declare @deleteCount bigint;
	-- 默认指示操作失败
	set @_success = 0;
	set @_message = N'';

	-- 验证参数 @administratorId (管理员用户 Id)
	if @administratorId is null
	begin
		set @_message = N'没有提供有效的 管理员用户 Id。';
		return;
	end

	-- 验证参数 @ids (消息 Id 列表)
	if @ids is null or @ids = ''
	begin
		set @_message = N'没有提供 消息 Id 列表。';
		return;
	end

	delete from 
		"waln_log"."opmessages" 
	where 
		charindex(cast("Id" as varchar(20)) + ',', @ids + ',') > 0; 

	set @deleteCount = rowcount_big();

	-- 指示操作成功
	set @_success = 1;

	if @deleteCount > 0
	begin
		-- 记录操作日志
		set @_log_body = concat(N'删除了 ', cast(@deleteCount as varchar(20)), ' 条数据操作日志记录。');
		exec "waln_log"."log_op"
			@administratorId, -- 当前用户的 Id
			8, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
			'waln_log.opmessages', -- 相关数据库表的名称列表
			'waln_log.p_mop_delete', -- 存储过程名称
			@_success, -- 指示操作是否成功
			@_log_body, -- 详细的操作描述
			@_context, -- 当前环境变量参数
			@_log_id out; -- 日志消息的 Id
	end
end
go
